grammar PalExpr;

options {
    output=AST;
    ASTLabelType=CommonTree; 
}

tokens {
    PRIV_REF;
    PRIV_NAMEAS;
    PRIV_MERGE;
    PRIV_COMPOSE;
    PRIV_EMPLOY;
    PRIV_RESTRICTION;
    PRIV_ATTACH;
    ENTITY_AND;
    ENTITY_OR;
    ENTITY_REF;
    BOOL_OR;
    BOOL_AND;
    RELATION_EQ;
    RELATION_NEQ;
    RELATION_GT;
    RELATION_GE;
    RELATION_LT;
    RELATION_LE;
    RELATION_CONFORM;
    RELATION_CATEGORY;
    PREDICATE_FUNC;
    PREDICATE_NAME;
    PREDICATE_VALUE;
    PREDICATE_PROPERTY;
    LET_ASSIGN;
    CATEGORY_SET;
    CATEGORY_UNSET;
    SCOPEBLOCK;
    STATEMENTS;
    NAMESPACE;
    NAME;
    NAMEPAIR;
    NAME_SUBSTITUTE;
    NAME_SUBSTITUTION;
}

@lexer::header {
package procilege.antlr;
}

@header {
package procilege.antlr;
}

prog
    : (statement) +
    ;

statement
    : privDefExpr
    | entityLetExpr
    | scopeBlock
    ;
    
scopeBlock
    : 'namespace' namespaceExpr (',' namespaceExpr)* '{' statement* '}' 
       ->^(SCOPEBLOCK namespaceExpr+ statement*)
    ;

entityLetExpr
    : 'let'! ( propertyAssignmentExpr | entityClassifyExpr )
    ;
    
propertyAssignmentExpr
    : e=entityPropertyExpr '=' v=predicateExpr 
         -> ^(LET_ASSIGN $e $v)
    ;
    
entityClassifyExpr
    : a=nameExpr 'is' ( 
         (b=nameExpr -> ^(CATEGORY_SET $a $b)) | 
         ('not' b=nameExpr -> ^(CATEGORY_UNSET $a $b)) )
    ;

privDefExpr
    : nameExpr ':=' privExpr -> ^(PRIV_NAMEAS nameExpr privExpr)
    ;
    
privExpr
    : (p1=privTerm -> privTerm) 
          ('+' p2=privExpr -> ^(PRIV_COMPOSE $p1 $p2))? 
    ;

privTerm
    : (p1=privEmpoly -> privEmpoly) 
    	   ( ( '*' p2=privTerm ) -> ^(PRIV_MERGE $p1 $p2) )?
    ;
    
privEmpoly
    : (p1=privRestrict -> privRestrict) 
    	( '/' et=entityTerm   -> ^(PRIV_MERGE $p1 $et) )?
    ;    
    
privRestrict
    : (p1=privSubstitute -> privSubstitute) 
    	( '[' ce=condExpr ']' -> ^(PRIV_MERGE $p1 $ce) )?
    ;    

privSubstitute
    : (expr=privElem -> privElem) ( 
         ('{' np+=substNamePair (',' np+=substNamePair)* '}')
         -> ^(NAME_SUBSTITUTE $expr $np+ )
      )?
    ; 

substNamePair
    :  ( (n1=nameExpr | '(' n2=privExpr ')' ) '/' o=nameExpr)   -> ^(NAMEPAIR $n1? $n2? $o)
    ;

privElem
    : nameExpr -> ^(PRIV_REF nameExpr)
    | '[' condExpr ']' -> ^(PRIV_MERGE condExpr)
    | '(' privExpr ')' -> privExpr
    ;

entityTerm
    : nameExpr -> ^(ENTITY_REF nameExpr)
    | '(' entityCondExpr ')' -> entityCondExpr
    ;

entityCondExpr
    :   ( entityCondAndExpression ( '||' entityCondAndExpression )*) 
    	-> ^(ENTITY_OR entityCondAndExpression*)
    ;

entityCondAndExpression
    :   ( entityTerm ( '&&' entityTerm )*) 
	-> ^(ENTITY_AND entityTerm*)
    ;
        
condExpr
    :   ( conditionalAndExpression ( '||' conditionalAndExpression )*) 
        -> ^(BOOL_OR conditionalAndExpression*)
    ;

conditionalAndExpression
    :   ( relationalExpr ( '&&' relationalExpr )*) 
        -> ^(BOOL_AND relationalExpr*)
    ;

relationalExpr
    : lt=predicateExpr (
              '==' rt=predicateExpr -> ^(RELATION_EQ $lt $rt)
            | '!=' rt=predicateExpr -> ^(RELATION_NEQ $lt $rt)
            | '>=' rt=predicateExpr -> ^(RELATION_GE $lt $rt)
            | '>'  rt=predicateExpr -> ^(RELATION_GT $lt $rt)
            | '<=' rt=predicateExpr -> ^(RELATION_LE $lt $rt)
            | '<'  rt=predicateExpr -> ^(RELATION_LT $lt $rt)
            | '/=' rt=predicateExpr -> ^(RELATION_CATEGORY $lt $rt)
            | 'is' rt=predicateExpr -> ^(RELATION_CATEGORY $lt $rt)
        | -> predicateExpr )
    ;

 
predicateExpr
    : predicateFuncExpr
    | n=nameExpr ( -> ^(PREDICATE_NAME $n) | '*~' rpv=privExpr -> ^(RELATION_CONFORM $n $rpv))
    | STRING_LITERAL  -> ^(PREDICATE_VALUE STRING_LITERAL)
    | DECIMAL_LITERAL -> ^(PREDICATE_VALUE DECIMAL_LITERAL)
    | entityPropertyExpr -> entityPropertyExpr
    | '(' condExpr ')' -> condExpr
    ; 
    
predicateFuncExpr
    : n=nameExpr '(' (i+=predicateExpr)? (',' i+=predicateExpr )*  ')'
        -> ^(PREDICATE_FUNC $n $i*)
    ; 
    
entityPropertyExpr
    : (preidacateEntityPropExpr -> preidacateEntityPropExpr)
    	('.' IDENTIFIER -> ^(PREDICATE_PROPERTY preidacateEntityPropExpr IDENTIFIER))*
    ;
    
preidacateEntityPropExpr             
    : (nameExpr '.' IDENTIFIER) -> ^(PREDICATE_PROPERTY nameExpr IDENTIFIER) 
    ;

namespaceExpr
    : (IDENTIFIER ':')? STRING_LITERAL -> ^(NAMESPACE STRING_LITERAL IDENTIFIER?)
    ;
    
nameExpr
    : (px=IDENTIFIER ':')? lp=IDENTIFIER -> ^(NAME $lp $px?)
    ;

IDENTIFIER
    :LETTER (LETTER|'0'..'9')*
    ;
    
fragment
LETTER
    : 'A'..'Z'
    | 'a'..'z'
    | '_'
    ;

STRING_LITERAL
    :  '"' ( EscapeSequence | ~('\\'|'"') )* '"'
    ;

DECIMAL_LITERAL
    : ('0' | '1'..'9' '0'..'9'*) ('.' ('0'..'9')+)? ;
    
fragment
EscapeSequence
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   OctalEscape
    ;
    
fragment
OctalEscape
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;    

WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {skip();}
    ;

COMMENT
    :   '/*' ( options {greedy=false;} : . )* '*/' {skip();} 
    ;

LINE_COMMENT
    : '//' ~('\n'|'\r')* '\r'? '\n' {skip();} 
    ;
